package edu.gatech.bsbmule.model;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;

import com.google.gson.Gson;

/**
 * This is a class that represents the game and contains details about number of
 * players, difficulty and current turn.
 * 
 * Uses Singleton
 * 
 * @author BSB
 * @version 1.0 10/27/2013
 */
public class Game {

	private List<Player> players;
	private int numberOfPlayers;
	private int playerCount;
	private int gameTurn;
	private int currentRound;
	private long[][] periods;
	private int difficulty;
	private Random randomGen;
	private Player loser;
	private int multiplier;

	private static List<Player> turnOrder;

	public static final int DIFFICULTY_BEGINNER = 0;
	public static final int DIFFICULTY_STANDARD = 1;
	public static final int DIFFICULTY_TOURNAMENT = 2;

	public static final int NORANDOMEVENT = -1;
	public static final int RANDOMEVENT_1 = 0;
	public static final int RANDOMEVENT_2 = 1;
	public static final int RANDOMEVENT_3 = 2;
	public static final int RANDOMEVENT_4 = 3;
	public static final int RANDOMEVENT_5 = 4;
	public static final int RANDOMEVENT_6 = 5;
	public static final int RANDOMEVENT_7 = 6;

	private static Game instance = null;
	private static final Gson gson = new Gson();

	/**
	 * Get instance method
	 * 
	 * @return instance
	 */
	public static Game getInstance() {
		if (instance == null) {
			instance = new Game();
		}
		return instance;
	}

	/**
	 * Constructor
	 */
	private Game() {
		players = new ArrayList<Player>();
		turnOrder = new ArrayList<Player>();
		numberOfPlayers = 0;
		difficulty = 0;
		currentRound = 1;
		gameTurn = 0;
		multiplier = 0;

		randomGen = new Random();
	}

	/**
	 * Gets lists of players
	 * 
	 * @return players
	 */
	public List<Player> getPlayers() {
		return players;
	}

	/**
	 * Sets players
	 * 
	 * @param List
	 *            <Player> players
	 */
	public void setPlayers(List<Player> players) {
		this.players = players;
	}

	/**
	 * Gets number of players
	 * 
	 * @return number of players
	 */
	public int getNumberOfPlayers() {
		return numberOfPlayers;
	}

	/**
	 * Sets number of players
	 * 
	 * @param int numberOfPlayers
	 */
	public void setNumberOfPlayers(int numberOfPlayers) {
		this.numberOfPlayers = numberOfPlayers;
	}

	/**
	 * Gets current player
	 * 
	 * @return current player
	 */
	public Player getCurrentPlayer() {
		return turnOrder.get(playerCount);
	}

	/**
	 * Gets player count
	 * 
	 * @return player count
	 */
	public int getPlayerCount() {
		return playerCount;
	}

	/**
	 * Moves to next player
	 */
	public void nextPlayer() {
		playerCount++;
		if (isTheLastPlayersTraversed()) {
			playerCount = 0;
			currentRound++;
		}
	}

	/**
	 * Checks if all players have gone
	 * 
	 * @return true if all player have gone
	 */
	public boolean isTheLastPlayersTraversed() {
		return (playerCount == numberOfPlayers);
	}

	/**
	 * Gets difficulty
	 * 
	 * @return difficulty
	 */
	public int getDifficulty() {
		return difficulty;
	}

	/**
	 * Sets difficulty
	 * 
	 * @param int difficulty
	 */
	public void setDifficulty(int difficulty) {
		this.difficulty = difficulty;
	}

	/**
	 * Gets game turn
	 * 
	 * @return game turn
	 */
	public int getGameTurn() {
		return gameTurn;
	}

	/**
	 * Sets game turn
	 * 
	 * @param int gameTurn
	 */
	public void setGameTurn(int gameTurn) {
		this.gameTurn = gameTurn;
	}

	/**
	 * Gets current round
	 * 
	 * @return current round
	 */
	public int getCurrentRound() {
		return currentRound;
	}

	/**
	 * Sets current round
	 * 
	 */
	public void setCurrentRound(int round) {
		currentRound = round;
	}

	/**
	 * Moves to next round
	 */
	public void nextRound() {
		currentRound++;
		for (Player p : players) {
			p.setFirstTimeFreeLand(true);
		}
		playerCount = 0;
	}

	/**
	 * Checks if current round is last round of game
	 * 
	 * @return true if last round
	 */
	public boolean isLastRound() {
		return (currentRound == gameTurn);
	}

	/**
	 * Checks if end of game
	 * 
	 * @return true if end of game
	 */
	public boolean isEndOfTheGame() {
		return (currentRound == gameTurn + 1);
	}

	/**
	 * Sets player turn order for each round, from lowest score to highest
	 */
	public void setTurnOrder() {
		turnOrder = new ArrayList<Player>();
		for (Player p : players) {
			if (turnOrder.isEmpty()) {
				turnOrder.add(p);
			} else {
				int i = 0;
				while ((i < turnOrder.size())
						&& (p.getScore() > turnOrder.get(i).getScore())) {
					i++;
				}
				if (i >= turnOrder.size()) {
					turnOrder.add(p);
				} else {
					turnOrder.add(i, p);
				}
			}
		}
		loser = turnOrder.get(0);
	}

	public Player getLoser() {
		return loser;
	}

	/**
	 * Creates turn order string
	 * 
	 * @return string
	 */
	public static String turnOrd() {
		String turnOrd = "[";
		for (Player p : turnOrder) {
			turnOrd = turnOrd + p.getName() + p.getScore() + " ";
		}
		turnOrd = turnOrd.substring(0, turnOrd.length() - 2) + "]";
		return turnOrd;
	}

	/**
	 * String representation of game info
	 * 
	 * @return string
	 */
	public String toString() {
		String gameInfo = "#Players: " + numberOfPlayers + " difficulty: "
				+ difficulty + " Turns: " + gameTurn + "\n" + "Times: "
				+ Arrays.toString(periods) + "\n";
		String playersInfo = "";
		for (Player p : players) {
			playersInfo += p.toString() + "\n";
		}
		return (gameInfo + "\n" + playersInfo);
	}

	/**
	 * Gets the next remaining time for player according to round and food
	 * 
	 * @return the next remaining time for this player
	 */
	public long getNextRemainingTime() {
		int round = Game.getInstance().getCurrentRound();
		int food = getCurrentPlayer().getFood();

		if (food == 0) {
			return 5000;
		} else if (round > 0 && round < 4) {
			if (food < 3) {
				return 30000;
			} else {
				return 50000;
			}
		} else if (round > 3 && round < 8) {
			if (food < 4) {
				return 30000;
			} else {
				return 50000;
			}
		} else {
			if (food < 5) {
				return 30000;
			} else {
				return 50000;
			}
		}
	}

	/**
	 * Gets random event
	 * 
	 * @return random event
	 */
	public int getRandomEvent() {
		int randNum = randomGen.nextInt(101);
		if (randNum <= 27) {
			int randomEvent = randomGen.nextInt(7);
			if (randomEvent == 0)
				return RANDOMEVENT_1;
			else if (randomEvent == 1)
				return RANDOMEVENT_2;
			else if (randomEvent == 2)
				return RANDOMEVENT_3;
			else if (randomEvent == 3)
				return RANDOMEVENT_4;
			else if (randomEvent == 4)
				return RANDOMEVENT_5;
			else if (randomEvent == 5)
				return RANDOMEVENT_6;
			else if (randomEvent == 6)
				return RANDOMEVENT_7;
		}
		return NORANDOMEVENT;
	}

	/**
	 * Performs random event results
	 * 
	 * @param int randomEvent
	 */
	public void randomEvent(int randomEvent) {
		if (this.getCurrentRound() < 4)
			multiplier = 25;
		else if (this.getCurrentRound() >= 4 && this.getCurrentRound() < 8)
			multiplier = 50;
		else if (this.getCurrentRound() >= 8 && this.getCurrentRound() < 12)
			multiplier = 75;
		else
			multiplier = 100;
		if (randomEvent == 0) {
			this.getCurrentPlayer().addFood(3);
			this.getCurrentPlayer().addEnergy(2);
		} else if (randomEvent == 1) {
			this.getCurrentPlayer().addSmithore(2);
		} else if (randomEvent == 2) {
			this.getCurrentPlayer().addMoney(8 * multiplier);
		} else if (randomEvent == 3) {
			this.getCurrentPlayer().addMoney(2 * multiplier);
		} else if (randomEvent == 4) {
			this.getCurrentPlayer().subtractMoney(4 * multiplier);
		} else if (randomEvent == 5) {
			int totalFood = this.getCurrentPlayer().getFood();
			this.getCurrentPlayer().subtractFood(totalFood / 2);
		} else if (randomEvent == 6) {
			this.getCurrentPlayer().subtractMoney(6 * multiplier);
		}
		System.out.println("After random event...");
		System.out.println(this.getCurrentPlayer().getInventory());
	}

	/**
	 * Gets random event int multiplier 
	 * 
	 * @return int multiplier 
	 */
	public int returnMultiplier() {
		return multiplier;
	}

	/**
	 * Initialize dimension of periods
	 * 
	 * @param dim
	 *            dimension of periods
	 */
	public void setPeriodsDim(int rounds, int playerCount) {
		periods = new long[rounds][playerCount];
	}

	/**
	 * Initialize all periods to 50s (for new game)
	 */
	public void initPeriods() {
		for (int i = 0; i < periods.length; ++i) {
			for (int j = 0 ; j < periods[0].length ; ++j) {
				periods[i][j] = 50000;
			}
		}
	}

	/**
	 * Get the period at a specific index
	 * 
	 * @param index
	 * @return period in milliseconds
	 */
	public long getPeriod(int round, int playerCount) {
		return periods[round][playerCount];
	}

	/**
	 * Get periods' dimension
	 * 
	 * @return periods' dimension
	 */
	public int getPeriodsLength() {
		return periods.length;
	}

	/**
	 * Update a period given the index and time in milliseconds
	 * 
	 * @param index
	 *            index
	 * @param time
	 *            time in milliseconds
	 */
	public void updatePeriod(int round, int playerCount, long time) {
		periods[round][playerCount] = time;
	}

	public static String saveToJson() {
		return gson.toJson(instance);
	}

	public static void loadFromJson(String json) {
		instance = gson.fromJson(json, Game.class);
	}
}
